//SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

use crate::get_conn_server_addr;
use crate::notice_decode::{decode_notice, new_wrong_session_notice};
use proto_gen_rust::user_api::user_api_client::UserApiClient;
use proto_gen_rust::user_api::*;
use std::time::Duration;
use tauri::async_runtime::Mutex;
use tauri::{App, AppHandle, Emitter, Manager, Runtime, WebviewWindow};
use tokio::time::sleep;
use tokio_stream::StreamExt;
use tonic::transport::Endpoint;

#[derive(Default)]
pub struct CurSession(pub Mutex<Option<String>>);

#[derive(Default)]
pub struct CurUserId(pub Mutex<Option<String>>);

async fn keep_alive_run<R: Runtime>(handle: &AppHandle<R>) {
    let mut session_id = String::from("");
    {
        let cur_value = handle.state::<CurSession>().inner();
        let cur_session = cur_value.0.lock().await;
        if let Some(cur_session_id) = cur_session.clone() {
            session_id.clone_from(&cur_session_id);
        }
    }
    if session_id != "" {
        if let Some(chan) = crate::get_grpc_chan(&handle).await {
            let mut client = UserApiClient::new(chan);
            let resp = client
                .keep_alive(KeepAliveRequest {
                    session_id: session_id,
                })
                .await;
            if let Err(err) = resp {
                println!("err {}", err);
            } else {
                let resp = resp.unwrap().into_inner();
                if resp.code != keep_alive_response::Code::Ok as i32 {
                    //清空session
                    {
                        let user_id = handle.state::<CurUserId>().inner();
                        *user_id.0.lock().await = None;
                        let user_session = handle.state::<CurSession>().inner();
                        *user_session.0.lock().await = None;
                    }
                    //发送通知
                    let res = handle.emit_to(
                        "main",
                        "notice",
                        new_wrong_session_notice("keep_alive".into()),
                    );
                    if res.is_err() {
                        println!("{:?}", res);
                    }
                }
            }
        }
    }
}

async fn keep_alive<R: Runtime>(app_handle: AppHandle<R>) {
    loop {
        sleep(Duration::from_secs(30)).await;
        keep_alive_run(&app_handle).await;
    }
}

async fn process_notice<R: Runtime>(
    app_handle: AppHandle<R>,
    endpoint: Endpoint,
    session_id: String,
) -> bool {
    let chan = endpoint
        .connect_timeout(Duration::from_secs(5))
        .tcp_keepalive(Some(Duration::from_secs(30)))
        .concurrency_limit(16)
        .buffer_size(1024 * 1024)
        .connect_lazy();
    let mut client = UserApiClient::new(chan);
    let stream = client
        .pull_notice(PullNoticeRequest {
            session_id: session_id.clone(),
        })
        .await;
    if stream.is_err() {
        return true;
    }
    let mut msg_stream = stream.unwrap().into_inner();
    while let Some(item) = msg_stream.next().await {
        if item.is_err() {
            return true;
        }
        let item = item.unwrap();
        if item.code != pull_notice_response::Code::Ok as i32 {
            return false;
        }
        {
            let cur_value = (&app_handle).state::<CurSession>().inner();
            let cur_session = cur_value.0.lock().await;
            let cur_session = cur_session.clone();
            if cur_session.is_none() {
                return false;
            }
            let cur_session = cur_session.unwrap();
            if cur_session != session_id {
                return false;
            }
        }
        if let Some(notice_data) = item.notice_data {
            if let Some(notice) = decode_notice(&notice_data) {
                let win_list = (&app_handle).webview_windows();
                let win_list = win_list.values();
                for win in win_list {
                    let label = win.label();
                    if label == "main" {
                        let _ = win.emit("notice", notice.clone());
                    }
                }
            }
        }
    }
    return true;
}

fn run_pull_notice<R: Runtime>(app_handle: AppHandle<R>, session_id: String) {
    tauri::async_runtime::spawn(async move {
        let server_addr = get_conn_server_addr(app_handle.clone()).await;
        let endpoint = Endpoint::from_shared(server_addr);
        if endpoint.is_err() {
            return;
        }
        let endpoint = endpoint.unwrap();
        loop {
            println!("start process notice");
            let retry =
                process_notice(app_handle.clone(), endpoint.clone(), session_id.clone()).await;
            if retry {
                sleep(Duration::from_secs(10)).await;
                println!("retry pull notice");
            } else {
                println!("exit pull notice");
                return;
            }
        }
    });
}

#[tauri::command]
pub async fn user_api_cmds_login<R: Runtime>(
    app_handle: AppHandle<R>,
    request: LoginRequest,
) -> Result<LoginResponse, String> {
    let chan = crate::get_grpc_chan(&app_handle).await;
    if (&chan).is_none() {
        return Err("no grpc conn".into());
    }
    let mut client = UserApiClient::new(chan.unwrap());
    match client.login(request).await {
        Ok(response) => {
            let ret = response.into_inner();
            let sess = app_handle.state::<CurSession>().inner();
            *sess.0.lock().await = Some(ret.session_id.clone());
            let user_info = ret.user_info.clone();
            if let Some(user_info) = user_info {
                let user_id = app_handle.state::<CurUserId>().inner();
                *user_id.0.lock().await = Some(user_info.user_id);
            }
            //监听推送信息
            run_pull_notice(app_handle, ret.session_id.clone());
            Ok(ret)
        }
        Err(status) => Err(status.message().into()),
    }
}

#[tauri::command]
pub async fn user_api_cmds_logout<R: Runtime>(
    app_handle: AppHandle<R>,
    request: LogoutRequest,
) -> Result<LogoutResponse, String> {
    let chan = crate::get_grpc_chan(&app_handle).await;
    if (&chan).is_none() {
        return Err("no grpc conn".into());
    }
    let mut client = UserApiClient::new(chan.unwrap());
    match client.logout(request).await {
        Ok(response) => {
            let sess = app_handle.state::<CurSession>().inner();
            *sess.0.lock().await = None;
            let user_id = app_handle.state::<CurUserId>().inner();
            *user_id.0.lock().await = None;

            //清除main以外的窗口
            let win_map = app_handle.webview_windows();
            for (label, win) in win_map.iter() {
                if label == "main" {
                    continue;
                }
                let _ = win.destroy();
            }
            Ok(response.into_inner())
        }
        Err(status) => Err(status.message().into()),
    }
}

#[tauri::command]
pub async fn user_api_cmds_update_basic<R: Runtime>(
    app_handle: AppHandle<R>,
    window: WebviewWindow<R>,
    request: UpdateBasicRequest,
) -> Result<UpdateBasicResponse, String> {
    let chan = crate::get_grpc_chan(&app_handle).await;
    if (&chan).is_none() {
        return Err("no grpc conn".into());
    }
    let mut client = UserApiClient::new(chan.unwrap());
    match client.update_basic(request).await {
        Ok(response) => {
            let inner_resp = response.into_inner();
            if inner_resp.code == update_basic_response::Code::WrongSession as i32 {
                if let Err(err) = window.emit("notice", new_wrong_session_notice("update".into())) {
                    println!("{:?}", err);
                }
            }
            return Ok(inner_resp);
        }
        Err(status) => Err(status.message().into()),
    }
}

#[tauri::command]
pub async fn user_api_cmds_change_passwd<R: Runtime>(
    app_handle: AppHandle<R>,
    window: WebviewWindow<R>,
    request: ChangePasswdRequest,
) -> Result<ChangePasswdResponse, String> {
    let chan = crate::get_grpc_chan(&app_handle).await;
    if (&chan).is_none() {
        return Err("no grpc conn".into());
    }
    let mut client = UserApiClient::new(chan.unwrap());
    match client.change_passwd(request).await {
        Ok(response) => {
            let inner_resp = response.into_inner();
            if inner_resp.code == change_passwd_response::Code::WrongSession as i32 {
                if let Err(err) =
                    window.emit("notice", new_wrong_session_notice("change_passwd".into()))
                {
                    println!("{:?}", err);
                }
            }
            return Ok(inner_resp);
        }
        Err(status) => Err(status.message().into()),
    }
}

#[tauri::command]
pub async fn user_api_cmds_register<R: Runtime>(
    app_handle: AppHandle<R>,
    request: RegisterRequest,
) -> Result<RegisterResponse, String> {
    let chan = crate::get_grpc_chan(&app_handle).await;
    if (&chan).is_none() {
        return Err("no grpc conn".into());
    }
    let mut client = UserApiClient::new(chan.unwrap());
    match client.register(request).await {
        Ok(response) => {
            let inner_resp = response.into_inner();
            return Ok(inner_resp);
        }
        Err(status) => Err(status.message().into()),
    }
}

#[tauri::command]
pub async fn user_api_cmds_get_session<R: Runtime>(app_handle: AppHandle<R>) -> String {
    let cur_value = app_handle.state::<CurSession>().inner();
    let cur_session = cur_value.0.lock().await;
    if let Some(cur_session) = cur_session.clone() {
        return cur_session;
    }
    return "".into();
}

#[tauri::command]
pub async fn user_api_cmds_get_user_id<R: Runtime>(app_handle: AppHandle<R>) -> String {
    let cur_value = app_handle.state::<CurUserId>().inner();
    let cur_user_id = cur_value.0.lock().await;
    if let Some(cur_user_id) = cur_user_id.clone() {
        return cur_user_id;
    }
    return "".into();
}

pub fn init(app: &App) {
    app.manage(CurSession(Default::default()));
    app.manage(CurUserId(Default::default()));

    let handle = app.handle().clone();
    tauri::async_runtime::spawn(async move {
        keep_alive(handle).await;
    });
}
